package com.liuyuncen.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.StopWatch;
import com.liuyuncen.entity.Feature;
import com.liuyuncen.entity.SimilarRequestDTO;
import com.liuyuncen.entity.Svo;
import com.liuyuncen.repository.EocRepository;
import com.liuyuncen.repository.SvoRepository;
import org.apache.commons.collections.CollectionUtils;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Session;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

import static org.neo4j.driver.Values.parameters;

/**
 * @Description 推荐controller
 * @Author xhl
 * @Date 2023/7/12 15:28
 **/
@RestController
@RequestMapping("recommend")
public class RecommendController {

    @Resource
    SvoRepository svoRepository;

    @Resource
    EocRepository eocRepository;

    @Resource
    Driver driver;

    /**
     * 查询相似车型码
     */
    @PostMapping("/queryTopSimilarNode")
    public String queryTopSimilarNode(@RequestBody SimilarRequestDTO request){

        //删除基准节点
        this.svoRepository.deleteBaseNodeRelationship();
        this.svoRepository.deleteBaseNode();

        List<String> result = new ArrayList<>();
        List<Feature> features = this.eocRepository.findFeatureByCode(request.getBaseSapCode());

        //如果有历史基础车型且能查到特征
        if(CollUtil.isNotEmpty(features)){
            //融合基础车型特征+用户描述的特征
            List<String> oldFeatures = features.stream().map(Feature::getName).collect(Collectors.toList());
            List<String> featureCodes = (List<String>) CollectionUtils.subtract(CollectionUtils.union(oldFeatures,request.getCustomFeatures()),
                    CollectionUtils.intersection(oldFeatures,request.getCustomFeatures()));

            //创建基准节点
            StopWatch stopWatch = new StopWatch();stopWatch.start();
            this.generateTestNode(featureCodes);
            stopWatch.stop();System.out.println("创建基准节点耗时：" + stopWatch.getTotalTimeMillis()/1000.0 + "s");

            //查询相似车型码
            stopWatch = new StopWatch();stopWatch.start();
            List<Map<String,Object>> nodeList = this.svoRepository.queryTopSimilarNode(request.getRecommendSize());
            stopWatch.stop();System.out.println("查询推荐车型码耗时：" + stopWatch.getTotalTimeMillis()/1000.0 + "ms");

            //查询相似车型码对应的订单号
            List<String> sapCodes = nodeList.stream().map(e -> (String)e.get("sapCode")).collect(Collectors.toList());
            List<Map<String,String>> orderList = this.svoRepository.queryOrderMap(sapCodes);
            Map<String,String> orderMap = new HashMap<>();
            orderList.forEach(e -> {
                orderMap.put(e.get("sapCode"),e.get("orderCode"));
            });
            System.out.println(orderList);

            //组装返回结果
            DecimalFormat df = new DecimalFormat("0.00");
            for(Map<String,Object> map : nodeList){
                String sapCode = (String) map.get("sapCode");
                Long commonSize = (Long) map.get("commonSize");
                String orderCode = orderMap.get(sapCode);
                result.add("推荐SVE车型码：" + sapCode + " 推荐SVE订单号：" + orderCode + " 推荐准确度：" + df.format(commonSize/1.0/featureCodes.size()*100) + "%" + "\n");
            }

            //删除基准节点
            this.svoRepository.deleteBaseNodeRelationship();
            this.svoRepository.deleteBaseNode();
        }else{
            //根据基础车型查不到特征的情况
        }
        return String.join("", result);
    }

    @PostMapping("/recommend/sapcode/byCypher")
    public String recommendWithCypher(@RequestBody List<String> featureCodes){
        List<String> result = new ArrayList<>();
        Map<String,Integer> map = new HashMap<>();
        //创建基准节点
//        this.generateTestNode(featureCodes);
        //所有sap码查出来
        List<String> allSvoCodes = this.svoRepository.getAllSvoCode();
        //遍历所有sap码，查询与基准节点共同的特征数量
        for(String sapCode : allSvoCodes){
            int commonCount = this.svoRepository.queryCommonFeatureNum(sapCode);
            map.put(sapCode,commonCount);
        }
        ArrayList<Map.Entry<String,Integer>> entries= sortMap(map);
        for( int i=0;i<3;i++){
            result.add(entries.get(i).getKey()+":" +entries.get(i).getValue());
            System.out.print(entries.get(i).getKey()+":" +entries.get(i).getValue());
        }

        return String.join(";", result);
    }

    /**
     * 生成基准节点
     * @param featureCodes 特征集合
     */
    private void generateTestNode(List<String> featureCodes){
        //创建基准节点
        Session session = driver.session();
        session.run("create (a:Svo{name:'base'})",parameters());
        for(String code:featureCodes){
            session.run("MATCH (a:Svo),(b:Feature) WHERE a.name='base' AND b.name=$name CREATE (a)-[follow:svo_feature]->(b) RETURN ID(follow)",parameters("name",code));
        }
        session.close();
    }

    @PostMapping("/recommend/sapcode/byFeatures")
    public String recommendSapCodeByFeature(@RequestBody List<String> featureCodes){
        List<String> result = new ArrayList<>();
        List<Svo> list = this.svoRepository.getAllSvo();
        Map<String,Integer> map = new HashMap<>();
        //遍历所有svo，获取特征，求特征交集
        for(Svo svo : list){
            List<Feature> features = this.svoRepository.findFeatureByCode(svo.getName());
            List<String> codes = features.stream().map(Feature::getName).collect(Collectors.toList());
            codes.retainAll(featureCodes);
            map.put(svo.getName(),codes.size());
        }
        System.out.println(map);
        ArrayList<Map.Entry<String,Integer>> entries= sortMap(map);
        for( int i=0;i<3;i++){
            result.add(entries.get(i).getKey()+":" +entries.get(i).getValue());
            System.out.print(entries.get(i).getKey()+":" +entries.get(i).getValue());
        }
        return String.join(";", result);
    }

    public static ArrayList<Map.Entry<String,Integer>> sortMap(Map map){
        List<Map.Entry<String, Integer>> entries = new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
        Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {
            public int compare(Map.Entry<String, Integer> obj1 , Map.Entry<String, Integer> obj2) {
                return obj2.getValue() - obj1.getValue();
            }
        });
        return (ArrayList<Map.Entry<String, Integer>>) entries;
    }
}
